
package com.scs.application.modules.upms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.scs.application.core.serial.SerialUtils;
import com.google.common.collect.Lists;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.serial.SerialRule;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.modules.upms.entity.SerialOwner;
import com.scs.application.modules.upms.mapper.SerialOwnerMapper;
import com.scs.application.modules.upms.mapper.SerialRuleMapper;
import com.scs.application.modules.upms.request.SerialRuleQueryRequest;
import com.scs.application.modules.upms.service.SerialRuleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * 编号规则 service 实现
 */
@Slf4j
@Service
public class SerialRuleServiceImpl extends BaseServiceImpl<SerialRuleMapper, SerialRule> implements SerialRuleService {

    private final String DEFAULT_TARGET = "default";

    private static final String DATETIME_KEY = "datetime_";
    private static final String SEQUENCE_KEY = "sequence_";
    private static final String STRING_KEY = "string_";

    @Autowired
    private SerialOwnerMapper serialOwnerMapper;

    @Override
    public List<SerialRule> list(SerialRuleQueryRequest request) {
        return this.list(getQueryWrapper(request)
        );
    }

    private QueryWrapper<SerialRule> getQueryWrapper(SerialRuleQueryRequest request) {
        return Wrappers.<SerialRule>query()
                .like(StringUtils.isNotBlank(request.getRuleCode()), "rule_code", request.getRuleCode())
                .like(StringUtils.isNotBlank(request.getName()), "name", request.getName());
    }

    @Override
    public IPage<SerialRule> page(SerialRuleQueryRequest request) {
        return this.page(request.getPage(),getQueryWrapper(request));
    }

    @Override
    public void syncToCache() {
        List<SerialRule> list = this.list();
        for (SerialRule rule : list) {
            SerialUtils.addSerialRule(rule);
        }
    }


    @Override
    public String generateCode(String ruleCode) {
        List<String> codes = batchGenerateCode(ruleCode, DEFAULT_TARGET, 1);
        return codes.isEmpty() ? null : codes.get(0);
    }

    @Override
    public String generateCode(String ruleCode, String target) {

        target = StringUtils.defaultString(target, DEFAULT_TARGET);

        List<String> codes = batchGenerateCode(ruleCode, target, 1);
        return codes.isEmpty() ? null : codes.get(0);
    }

    @Override
    public List<String> batchGenerateCode(String ruleCode, int count) {
        return batchGenerateCode(ruleCode,DEFAULT_TARGET,count);
    }


    @Override
    @Transactional
    public List<String> batchGenerateCode(String ruleCode, String target, int count) {
        count = count <= 0 ? 1 : count;
        target = StringUtils.defaultString(target, DEFAULT_TARGET);
        String lockKey = ruleCode + target;

        SerialRule serialRule = this.getOne(Wrappers.<SerialRule>query().eq("rule_code", ruleCode).last("limit 1"));
        if (serialRule == null) {
            throw new BusinessException("编号规则[" + ruleCode + "]未找到，请确认是否配置");
        }

        List<String> codes = Lists.newArrayList();

        String ruleId = serialRule.getId();

        synchronized (lockKey.intern()) {

            SerialOwner owner = serialOwnerMapper.selectOne(
                    Wrappers.<SerialOwner>query()
                            .eq("rule_id", ruleId)
                            .eq("target", target)
                            .last("limit 1")
            );
            String currentCode = owner != null ? StringUtils.defaultString(owner.getCurrentCode()) : "";
            if (owner == null) {
                owner = new SerialOwner();
                owner.setTarget(target);
                owner.setRuleId(ruleId);
                owner.setTarget(target);
            }

            int partNum = serialRule.getPartNum();
            String newCode = "";

            String delimiter = StringUtils.defaultString(serialRule.getDelimiter());

            String[] parts = new String[]{serialRule.getPart1(), StringUtils.defaultString(serialRule.getPart2()),
                    StringUtils.defaultString(serialRule.getPart3()), StringUtils.defaultString(serialRule.getPart4())};
            String[] partConds = new String[]{serialRule.getPart1Cond(),
                    StringUtils.defaultString(serialRule.getPart2Cond()),
                    StringUtils.defaultString(serialRule.getPart3Cond()),
                    StringUtils.defaultString(serialRule.getPart4Cond())};

            int totalLength = 0;
            for (int index = 0; index < partNum; index++) {
                String partCond = partConds[index];
                if (partCond.startsWith(STRING_KEY)) {
                    totalLength += parts[index].length();
                } else if (partCond.startsWith(DATETIME_KEY)) {
                    totalLength += getDatePatterByPartCond(partConds[index]).length();
                } else if (partCond.startsWith(SEQUENCE_KEY)) {
                    totalLength += Integer.parseInt(partConds[index].substring(SEQUENCE_KEY.length()));
                }
                if (index != partNum - 1) {
                    totalLength += StringUtils.isBlank(delimiter) ? 0 : 1;
                }
            }
            Date now = new Date();


            if (StringUtils.isNotBlank(currentCode)) {
                if (totalLength != currentCode.length()) {
                    log.error("编号规则[{}]当前编码长度：{}，预期编码长度：{}，生成编码失败", ruleCode, currentCode.length(),
                            totalLength);
                    throw new RuntimeException("编号规则[" + ruleCode + "]可能在使用之后规则重新变更，请联系管理员");
                }
            }
            for (int i = 0; i < count; i++) {
                newCode = "";
                if (StringUtils.isBlank(currentCode)) {
                    for (int index = 0; index < partNum; index++) {
                        String partCond = partConds[index];
                        String partValue = parts[index];
                        if (partCond.startsWith(STRING_KEY)) {
                            newCode += partValue;
                        } else if (partCond.startsWith(DATETIME_KEY)) {
                            newCode += DateUtils.format(now, getDatePatterByPartCond(partConds[index]));
                        } else if (partCond.startsWith(SEQUENCE_KEY)) {
                            int partLength = Integer.parseInt(partConds[index].substring(SEQUENCE_KEY.length()));

                            // 序列初始值
                            if (StringUtils.isNotBlank(partValue)) {
                                newCode += StringUtils.repeat("0", (partLength - (partValue.length()))) + partValue;
                            } else {
                                newCode += StringUtils.repeat("0", (partLength - 1)) + "1";
                            }
                        } else {
                            throw new RuntimeException("编号规则[" + ruleCode + "]配置异常，请联系管理员");
                        }
                        if (index != partNum - 1) {
                            newCode += delimiter;
                        }
                    }
                    if (StringUtils.isBlank(newCode)) {
                        throw new RuntimeException("编号规则[" + ruleCode + "]  编码生成失败，请联系管理员");
                    }
                    currentCode = newCode;
                    codes.add(currentCode);
                    continue;
                }

                String[] partValues = new String[partNum];

                /**
                 * 对当前编码进行解析，存入数组
                 */
                if (StringUtils.isBlank(delimiter)) {
                    int beginIndex = 0;
                    int endIndex = 0;
                    for (int j = 0; j < partNum; j++) {
                        String partCond = partConds[j];
                        if (partCond.startsWith(STRING_KEY)) {
                            partValues[j] = parts[j];
                            beginIndex += partValues[j].length();
                            endIndex += partValues[j].length();
                            continue;
                        }
                        if (partCond.startsWith(DATETIME_KEY)) {
                            endIndex += getDatePatterByPartCond(partCond).length();
                        } else if (partCond.startsWith(SEQUENCE_KEY)) {
                            endIndex += Integer.parseInt(partCond.substring(SEQUENCE_KEY.length()));
                        }
                        partValues[j] = currentCode.substring(beginIndex, endIndex);
                        beginIndex += partValues[j].length();
                    }
                } else {
                    partValues = StringUtils.split(currentCode, delimiter);
                }

                boolean dateChanged = false;
                /**
                 * 判断时间部分是否改变
                 */
                for (int index = 0; index < partNum; index++) {
                    String partCond = partConds[index];
                    if (partCond.startsWith(DATETIME_KEY)) {
                        String value = DateUtils.format(now, getDatePatterByPartCond((partCond)));
                        if (!value.equals(partValues[index])) {
                            dateChanged = true;
                            break;
                        }
                    }
                }

                for (int index = 0; index < partNum; index++) {
                    String part = parts[index];
                    String partCond = partConds[index];
                    String partValue = "";
                    if (StringUtils.isBlank(partCond)) {
                        throw new RuntimeException("编码规则[" + ruleCode + "]配置异常，请联系管理员");
                    }

                    if (partCond.startsWith(STRING_KEY)) {
                        partValue = part;
                    } else if (partCond.startsWith(SEQUENCE_KEY)) {

                        int partLength = Integer.parseInt(partCond.substring(SEQUENCE_KEY.length()));
                        int partValuePre = Integer.parseInt(partValues[index]);

                        // 如果日期改变，序号从1开始
                        if (dateChanged) {
                            partValue = StringUtils.repeat("0", (partLength - 1)) + "1";
                        } else {

                            int maxValue = Integer.parseInt(StringUtils.repeat("9", partLength));
                            if (partValuePre >= maxValue) {
                                throw new RuntimeException(
                                        "数据量超出编码自增长上限值，请扩大编号规则[" + ruleCode + "]的序列自增长范围，或联系管理员处理");
                            } else {
                                int partValueNow = partValuePre + 1;
                                partValue = StringUtils.repeat("0", partLength - String.valueOf(partValueNow).length())
                                        + partValueNow;
                            }
                        }
                    } else if (partCond.startsWith(DATETIME_KEY)) {
                        partValue = DateUtils.format(now, getDatePatterByPartCond(partCond));
                    }
                    newCode += partValue + (index != partNum - 1 ? delimiter : "");
                }
                currentCode = newCode;
                codes.add(currentCode);
            }

            if (codes.isEmpty()) {
                throw new BusinessException("编号规则[" + ruleCode + "]  编码生成失败，请联系管理员");
            }
            owner.setCurrentCode(codes.get(codes.size() - 1));
            insertOrUpdateOwner(owner);
        }

        return codes;
    }


    private boolean insertOrUpdateOwner(SerialOwner owner) {
        if (StringUtils.isNotBlank(owner.getId())) {
            return serialOwnerMapper.updateById(owner) != 0;
        } else {
            return serialOwnerMapper.insert(owner) != 0;
        }
    }

    private String getDatePatterByPartCond(String partCond) {
        String pattern = partCond.substring("datetime_".length());
        return pattern;
    }


}
